package com.baiye.www.spring.ioc.factory;

import com.baiye.www.spring.ioc.BeanPostProcessor;
import com.baiye.www.spring.ioc.bean.BeanDefinition;
import com.baiye.www.spring.ioc.bean.BeanReference;
import com.baiye.www.spring.ioc.bean.PropertyValue;
import com.baiye.www.spring.ioc.bean.xml.XmlBeanDefinitionReader;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author baiye
 * @version 1.0
 * @date 2021/12/11 17:38
 */
public class XmlBeanFactory implements BeanFactory {
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    private List<String> beanDefinitionNames = new ArrayList<>();

    //存放BeanPostProcessor的实现类
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
    private XmlBeanDefinitionReader beanDefinitionReader;

    public XmlBeanFactory(String ConfigurationFile) throws Exception {
        beanDefinitionReader = new XmlBeanDefinitionReader();
        loaBeanDefinitions(ConfigurationFile);
    }

    private void loaBeanDefinitions(String ConfigurationFile) throws Exception {
        //注册进 beanDefinitionReader 的register map
        beanDefinitionReader.loadBeanDefinitions(ConfigurationFile);

        //将register map的东西放入beanDefinitionMap和beanDefinitionNames
        registerBeanDefinition();

        //将BeanPostProcessor的实现类所有放到beanPostProcessors中
        registerBeanPostProcessor();
    }

    //将BeanPostProcessor的实现类所有放到beanPostProcessors中
    private void registerBeanPostProcessor() throws Exception {
        List beans = getBeansForType(BeanPostProcessor.class);
        for (Object bean : beans) {
            beanPostProcessors.add((BeanPostProcessor)bean);
        }
    }


    public List getBeansForType(Class clazz) throws Exception {
        List beans = new ArrayList();
        for (String beanName : beanDefinitionNames) {
            if (clazz.isAssignableFrom(beanDefinitionMap.get(beanName).getBeanClass())) {
                beans.add(getBean(beanName));
            }
        }
        return beans;
    }

    private void registerBeanDefinition() {
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionReader.getRegistry().entrySet()
        ) {
            String name = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            beanDefinitionMap.put(name, beanDefinition);
            beanDefinitionNames.add(name);
        }
    }

    @Override
    public Object getBean(String beanName) throws Exception {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new RuntimeException("no this bean with name " + beanName);
        }
        //延迟加载
        Object bean = beanDefinition.getBean();
        if (bean == null) {
            bean = createBean(beanDefinition);
            bean = initializeBean(bean, beanName);
            beanDefinition.setBean(bean);
        }
        return bean;
    }

    // 初始化会将那些那些实现BeanPostProcessor接口的类生成代理对象
    private Object initializeBean(Object bean, String beanName) throws Exception {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
        }
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
        }

        return bean;
    }

    private Object createBean(BeanDefinition beanDefinition) throws Exception {
        // 根据类信息，直接实例化一个对象
        Object bean = beanDefinition.getBeanClass().getDeclaredConstructor().newInstance();
        // 填充属性值
        applyPropertyValues(bean, beanDefinition);

        return bean;
    }

    private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
        // 检查 bean 对象是否实现了 Aware 一类的接口，如果实现了则把相应的依赖设置到 bean 对象中。
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }

        for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValueList()) {
            Object value = propertyValue.getValue();
            // 判断是不是引用对象，如果是，则先进行初始化并获得
            if (value instanceof BeanReference) {
                BeanReference beanReference = (BeanReference) value;
                value = getBean(beanReference.getName());
            }

            try {
                // propertyValuesList中的每个值都是一个PropertyValue键值对，对应要封装的属性名称和值
                // 通过set + 名称大写 字符串拼接后，得到属性名称的set方法的使用权，进行填充值
                Method declaredMethod = bean.getClass().getDeclaredMethod(
                        "set" + propertyValue.getName().substring(0, 1).toUpperCase()
                                + propertyValue.getName().substring(1), value.getClass());
                declaredMethod.setAccessible(true);

                declaredMethod.invoke(bean, value);
            } catch (NoSuchMethodException e) {
                // 如果没有这个属性的set方法
                // 就通过反射，获得属性的访问权，进行赋值
                Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
                declaredField.setAccessible(true);
                declaredField.set(bean, value);
            }
        }
    }
}
